home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / adodb / adodb-xmlschema.inc.php < prev    next >
PHP Script  |  2005-01-24  |  57KB  |  2,194 lines

  1. <?php
  2. // Copyright (c) 2004 ars Cognita Inc., all rights reserved
  3. /* ******************************************************************************
  4.     Released under both BSD license and Lesser GPL library license. 
  5.      Whenever there is any discrepancy between the two licenses, 
  6.      the BSD license will take precedence. 
  7. *******************************************************************************/
  8. /**
  9.  * xmlschema is a class that allows the user to quickly and easily
  10.  * build a database on any ADOdb-supported platform using a simple
  11.  * XML schema.
  12.  *
  13.  * Last Editor: $Author: jlim $
  14.  * @author Richard Tango-Lowy & Dan Cech
  15.  * @version $Revision: 1.11 $
  16.  *
  17.  * @package axmls
  18.  * @tutorial getting_started.pkg
  19.  */
  20.  
  21. /**
  22. * Debug on or off
  23. */
  24. if( !defined( 'XMLS_DEBUG' ) ) {
  25.     define( 'XMLS_DEBUG', FALSE );
  26. }
  27.  
  28. /**
  29. * Default prefix key
  30. */
  31. if( !defined( 'XMLS_PREFIX' ) ) {
  32.     define( 'XMLS_PREFIX', '%%P' );
  33. }
  34.  
  35. /**
  36. * Maximum length allowed for object prefix
  37. */
  38. if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
  39.     define( 'XMLS_PREFIX_MAXLEN', 10 );
  40. }
  41.  
  42. /**
  43. * Execute SQL inline as it is generated
  44. */
  45. if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
  46.     define( 'XMLS_EXECUTE_INLINE', FALSE );
  47. }
  48.  
  49. /**
  50. * Continue SQL Execution if an error occurs?
  51. */
  52. if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
  53.     define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
  54. }
  55.  
  56. /**
  57. * Current Schema Version
  58. */
  59. if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
  60.     define( 'XMLS_SCHEMA_VERSION', '0.2' );
  61. }
  62.  
  63. /**
  64. * Default Schema Version.  Used for Schemas without an explicit version set.
  65. */
  66. if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
  67.     define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
  68. }
  69.  
  70. /**
  71. * Default Schema Version.  Used for Schemas without an explicit version set.
  72. */
  73. if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
  74.     define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
  75. }
  76.  
  77. /**
  78. * Include the main ADODB library
  79. */
  80. if( !defined( '_ADODB_LAYER' ) ) {
  81.     require( 'adodb.inc.php' );
  82.     require( 'adodb-datadict.inc.php' );
  83. }
  84.  
  85. /**
  86. * Abstract DB Object. This class provides basic methods for database objects, such
  87. * as tables and indexes.
  88. *
  89. * @package axmls
  90. * @access private
  91. */
  92. class dbObject {
  93.     
  94.     /**
  95.     * var object Parent
  96.     */
  97.     var $parent;
  98.     
  99.     /**
  100.     * var string current element
  101.     */
  102.     var $currentElement;
  103.     
  104.     /**
  105.     * NOP
  106.     */
  107.     function dbObject( &$parent, $attributes = NULL ) {
  108.         $this->parent =& $parent;
  109.     }
  110.     
  111.     /**
  112.     * XML Callback to process start elements
  113.     *
  114.     * @access private
  115.     */
  116.     function _tag_open( &$parser, $tag, $attributes ) {
  117.         
  118.     }
  119.     
  120.     /**
  121.     * XML Callback to process CDATA elements
  122.     *
  123.     * @access private
  124.     */
  125.     function _tag_cdata( &$parser, $cdata ) {
  126.         
  127.     }
  128.     
  129.     /**
  130.     * XML Callback to process end elements
  131.     *
  132.     * @access private
  133.     */
  134.     function _tag_close( &$parser, $tag ) {
  135.         
  136.     }
  137.     
  138.     function create() {
  139.         return array();
  140.     }
  141.     
  142.     /**
  143.     * Destroys the object
  144.     */
  145.     function destroy() {
  146.         unset( $this );
  147.     }
  148.     
  149.     /**
  150.     * Checks whether the specified RDBMS is supported by the current
  151.     * database object or its ranking ancestor.
  152.     *
  153.     * @param string $platform RDBMS platform name (from ADODB platform list).
  154.     * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
  155.     */
  156.     function supportedPlatform( $platform = NULL ) {
  157.         return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
  158.     }
  159.     
  160.     /**
  161.     * Returns the prefix set by the ranking ancestor of the database object.
  162.     *
  163.     * @param string $name Prefix string.
  164.     * @return string Prefix.
  165.     */
  166.     function prefix( $name = '' ) {
  167.         return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
  168.     }
  169.     
  170.     /**
  171.     * Extracts a field ID from the specified field.
  172.     *
  173.     * @param string $field Field.
  174.     * @return string Field ID.
  175.     */
  176.     function FieldID( $field ) {
  177.         return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
  178.     }
  179. }
  180.  
  181. /**
  182. * Creates a table object in ADOdb's datadict format
  183. *
  184. * This class stores information about a database table. As charactaristics
  185. * of the table are loaded from the external source, methods and properties
  186. * of this class are used to build up the table description in ADOdb's
  187. * datadict format.
  188. *
  189. * @package axmls
  190. * @access private
  191. */
  192. class dbTable extends dbObject {
  193.     
  194.     /**
  195.     * @var string Table name
  196.     */
  197.     var $name;
  198.     
  199.     /**
  200.     * @var array Field specifier: Meta-information about each field
  201.     */
  202.     var $fields = array();
  203.     
  204.     /**
  205.     * @var array List of table indexes.
  206.     */
  207.     var $indexes = array();
  208.     
  209.     /**
  210.     * @var array Table options: Table-level options
  211.     */
  212.     var $opts = array();
  213.     
  214.     /**
  215.     * @var string Field index: Keeps track of which field is currently being processed
  216.     */
  217.     var $current_field;
  218.     
  219.     /**
  220.     * @var boolean Mark table for destruction
  221.     * @access private
  222.     */
  223.     var $drop_table;
  224.     
  225.     /**
  226.     * @var boolean Mark field for destruction (not yet implemented)
  227.     * @access private
  228.     */
  229.     var $drop_field = array();
  230.     
  231.     /**
  232.     * Iniitializes a new table object.
  233.     *
  234.     * @param string $prefix DB Object prefix
  235.     * @param array $attributes Array of table attributes.
  236.     */
  237.     function dbTable( &$parent, $attributes = NULL ) {
  238.         $this->parent =& $parent;
  239.         $this->name = $this->prefix($attributes['NAME']);
  240.     }
  241.     
  242.     /**
  243.     * XML Callback to process start elements. Elements currently 
  244.     * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. 
  245.     *
  246.     * @access private
  247.     */
  248.     function _tag_open( &$parser, $tag, $attributes ) {
  249.         $this->currentElement = strtoupper( $tag );
  250.         
  251.         switch( $this->currentElement ) {
  252.             case 'INDEX':
  253.                 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  254.                     xml_set_object( $parser, $this->addIndex( $attributes ) );
  255.                 }
  256.                 break;
  257.             case 'DATA':
  258.                 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  259.                     xml_set_object( $parser, $this->addData( $attributes ) );
  260.                 }
  261.                 break;
  262.             case 'DROP':
  263.                 $this->drop();
  264.                 break;
  265.             case 'FIELD':
  266.                 // Add a field
  267.                 $fieldName = $attributes['NAME'];
  268.                 $fieldType = $attributes['TYPE'];
  269.                 $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
  270.                 $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
  271.                 
  272.                 $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
  273.                 break;
  274.             case 'KEY':
  275.             case 'NOTNULL':
  276.             case 'AUTOINCREMENT':
  277.                 // Add a field option
  278.                 $this->addFieldOpt( $this->current_field, $this->currentElement );
  279.                 break;
  280.             case 'DEFAULT':
  281.                 // Add a field option to the table object
  282.                 
  283.                 // Work around ADOdb datadict issue that misinterprets empty strings.
  284.                 if( $attributes['VALUE'] == '' ) {
  285.                     $attributes['VALUE'] = " '' ";
  286.                 }
  287.                 
  288.                 $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
  289.                 break;
  290.             case 'DEFDATE':
  291.             case 'DEFTIMESTAMP':
  292.                 // Add a field option to the table object
  293.                 $this->addFieldOpt( $this->current_field, $this->currentElement );
  294.                 break;
  295.             default:
  296.                 // print_r( array( $tag, $attributes ) );
  297.         }
  298.     }
  299.     
  300.     /**
  301.     * XML Callback to process CDATA elements
  302.     *
  303.     * @access private
  304.     */
  305.     function _tag_cdata( &$parser, $cdata ) {
  306.         switch( $this->currentElement ) {
  307.             // Table constraint
  308.             case 'CONSTRAINT':
  309.                 if( isset( $this->current_field ) ) {
  310.                     $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
  311.                 } else {
  312.                     $this->addTableOpt( $cdata );
  313.                 }
  314.                 break;
  315.             // Table option
  316.             case 'OPT':
  317.                 $this->addTableOpt( $cdata );
  318.                 break;
  319.             default:
  320.                 
  321.         }
  322.     }
  323.     
  324.     /**
  325.     * XML Callback to process end elements
  326.     *
  327.     * @access private
  328.     */
  329.     function _tag_close( &$parser, $tag ) {
  330.         $this->currentElement = '';
  331.         
  332.         switch( strtoupper( $tag ) ) {
  333.             case 'TABLE':
  334.                 $this->parent->addSQL( $this->create( $this->parent ) );
  335.                 xml_set_object( $parser, $this->parent );
  336.                 $this->destroy();
  337.                 break;
  338.             case 'FIELD':
  339.                 unset($this->current_field);
  340.                 break;
  341.  
  342.         }
  343.     }
  344.     
  345.     /**
  346.     * Adds an index to a table object
  347.     *
  348.     * @param array $attributes Index attributes
  349.     * @return object dbIndex object
  350.     */
  351.     function &addIndex( $attributes ) {
  352.         $name = strtoupper( $attributes['NAME'] );
  353.         $this->indexes[$name] =& new dbIndex( $this, $attributes );
  354.         return $this->indexes[$name];
  355.     }
  356.     
  357.     /**
  358.     * Adds data to a table object
  359.     *
  360.     * @param array $attributes Data attributes
  361.     * @return object dbData object
  362.     */
  363.     function &addData( $attributes ) {
  364.         if( !isset( $this->data ) ) {
  365.             $this->data =& new dbData( $this, $attributes );
  366.         }
  367.         return $this->data;
  368.     }
  369.     
  370.     /**
  371.     * Adds a field to a table object
  372.     *
  373.     * $name is the name of the table to which the field should be added. 
  374.     * $type is an ADODB datadict field type. The following field types
  375.     * are supported as of ADODB 3.40:
  376.     *     - C:  varchar
  377.     *    - X:  CLOB (character large object) or largest varchar size
  378.     *       if CLOB is not supported
  379.     *    - C2: Multibyte varchar
  380.     *    - X2: Multibyte CLOB
  381.     *    - B:  BLOB (binary large object)
  382.     *    - D:  Date (some databases do not support this, and we return a datetime type)
  383.     *    - T:  Datetime or Timestamp
  384.     *    - L:  Integer field suitable for storing booleans (0 or 1)
  385.     *    - I:  Integer (mapped to I4)
  386.     *    - I1: 1-byte integer
  387.     *    - I2: 2-byte integer
  388.     *    - I4: 4-byte integer
  389.     *    - I8: 8-byte integer
  390.     *    - F:  Floating point number
  391.     *    - N:  Numeric or decimal number
  392.     *
  393.     * @param string $name Name of the table to which the field will be added.
  394.     * @param string $type    ADODB datadict field type.
  395.     * @param string $size    Field size
  396.     * @param array $opts    Field options array
  397.     * @return array Field specifier array
  398.     */
  399.     function addField( $name, $type, $size = NULL, $opts = NULL ) {
  400.         $field_id = $this->FieldID( $name );
  401.         
  402.         // Set the field index so we know where we are
  403.         $this->current_field = $field_id;
  404.         
  405.         // Set the field name (required)
  406.         $this->fields[$field_id]['NAME'] = $name;
  407.         
  408.         // Set the field type (required)
  409.         $this->fields[$field_id]['TYPE'] = $type;
  410.         
  411.         // Set the field size (optional)
  412.         if( isset( $size ) ) {
  413.             $this->fields[$field_id]['SIZE'] = $size;
  414.         }
  415.         
  416.         // Set the field options
  417.         if( isset( $opts ) ) {
  418.             $this->fields[$field_id]['OPTS'][] = $opts;
  419.         }
  420.     }
  421.     
  422.     /**
  423.     * Adds a field option to the current field specifier
  424.     *
  425.     * This method adds a field option allowed by the ADOdb datadict 
  426.     * and appends it to the given field.
  427.     *
  428.     * @param string $field    Field name
  429.     * @param string $opt ADOdb field option
  430.     * @param mixed $value Field option value
  431.     * @return array Field specifier array
  432.     */
  433.     function addFieldOpt( $field, $opt, $value = NULL ) {
  434.         if( !isset( $value ) ) {
  435.             $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
  436.         // Add the option and value
  437.         } else {
  438.             $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
  439.         }
  440.     }
  441.     
  442.     /**
  443.     * Adds an option to the table
  444.     *
  445.     * This method takes a comma-separated list of table-level options
  446.     * and appends them to the table object.
  447.     *
  448.     * @param string $opt Table option
  449.     * @return array Options
  450.     */
  451.     function addTableOpt( $opt ) {
  452.         $this->opts[] = $opt;
  453.         
  454.         return $this->opts;
  455.     }
  456.     
  457.     /**
  458.     * Generates the SQL that will create the table in the database
  459.     *
  460.     * @param object $xmls adoSchema object
  461.     * @return array Array containing table creation SQL
  462.     */
  463.     function create( &$xmls ) {
  464.         $sql = array();
  465.         
  466.         // drop any existing indexes
  467.         if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
  468.             foreach( $legacy_indexes as $index => $index_details ) {
  469.                 $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
  470.             }
  471.         }
  472.         
  473.         // remove fields to be dropped from table object
  474.         foreach( $this->drop_field as $field ) {
  475.             unset( $this->fields[$field] );
  476.         }
  477.         
  478.         // if table exists
  479.         if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
  480.             // drop table
  481.             if( $this->drop_table ) {
  482.                 $sql[] = $xmls->dict->DropTableSQL( $this->name );
  483.                 
  484.                 return $sql;
  485.             }
  486.             
  487.             // drop any existing fields not in schema
  488.             foreach( $legacy_fields as $field_id => $field ) {
  489.                 if( !isset( $this->fields[$field_id] ) ) {
  490.                     $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' );
  491.                 }
  492.             }
  493.         // if table doesn't exist
  494.         } else {
  495.             if( $this->drop_table ) {
  496.                 return $sql;
  497.             }
  498.             
  499.             $legacy_fields = array();
  500.         }
  501.         
  502.         // Loop through the field specifier array, building the associative array for the field options
  503.         $fldarray = array();
  504.         
  505.         foreach( $this->fields as $field_id => $finfo ) {
  506.             // Set an empty size if it isn't supplied
  507.             if( !isset( $finfo['SIZE'] ) ) {
  508.                 $finfo['SIZE'] = '';
  509.             }
  510.             
  511.             // Initialize the field array with the type and size
  512.             $fldarray[$field_id] = array(
  513.                 'NAME' => $finfo['NAME'],
  514.                 'TYPE' => $finfo['TYPE'],
  515.                 'SIZE' => $finfo['SIZE']
  516.             );
  517.             
  518.             // Loop through the options array and add the field options. 
  519.             if( isset( $finfo['OPTS'] ) ) {
  520.                 foreach( $finfo['OPTS'] as $opt ) {
  521.                     // Option has an argument.
  522.                     if( is_array( $opt ) ) {
  523.                         $key = key( $opt );
  524.                         $value = $opt[key( $opt )];
  525.                         @$fldarray[$field_id][$key] .= $value;
  526.                     // Option doesn't have arguments
  527.                     } else {
  528.                         $fldarray[$field_id][$opt] = $opt;
  529.                     }
  530.                 }
  531.             }
  532.         }
  533.         
  534.         if( empty( $legacy_fields ) ) {
  535.             // Create the new table
  536.             $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
  537.             logMsg( end( $sql ), 'Generated CreateTableSQL' );
  538.         } else {
  539.             // Upgrade an existing table
  540.             logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
  541.             switch( $xmls->upgrade ) {
  542.                 // Use ChangeTableSQL
  543.                 case 'ALTER':
  544.                     logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
  545.                     $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
  546.                     break;
  547.                 case 'REPLACE':
  548.                     logMsg( 'Doing upgrade REPLACE (testing)' );
  549.                     $sql[] = $xmls->dict->DropTableSQL( $this->name );
  550.                     $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
  551.                     break;
  552.                 // ignore table
  553.                 default:
  554.                     return array();
  555.             }
  556.         }
  557.         
  558.         foreach( $this->indexes as $index ) {
  559.             $sql[] = $index->create( $xmls );
  560.         }
  561.         
  562.         if( isset( $this->data ) ) {
  563.             $sql[] = $this->data->create( $xmls );
  564.         }
  565.         
  566.         return $sql;
  567.     }
  568.     
  569.     /**
  570.     * Marks a field or table for destruction
  571.     */
  572.     function drop() {
  573.         if( isset( $this->current_field ) ) {
  574.             // Drop the current field
  575.             logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
  576.             // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
  577.             $this->drop_field[$this->current_field] = $this->current_field;
  578.         } else {
  579.             // Drop the current table
  580.             logMsg( "Dropping table '{$this->name}'" );
  581.             // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
  582.             $this->drop_table = TRUE;
  583.         }
  584.     }
  585. }
  586.  
  587. /**
  588. * Creates an index object in ADOdb's datadict format
  589. *
  590. * This class stores information about a database index. As charactaristics
  591. * of the index are loaded from the external source, methods and properties
  592. * of this class are used to build up the index description in ADOdb's
  593. * datadict format.
  594. *
  595. * @package axmls
  596. * @access private
  597. */
  598. class dbIndex extends dbObject {
  599.     
  600.     /**
  601.     * @var string    Index name
  602.     */
  603.     var $name;
  604.     
  605.     /**
  606.     * @var array    Index options: Index-level options
  607.     */
  608.     var $opts = array();
  609.     
  610.     /**
  611.     * @var array    Indexed fields: Table columns included in this index
  612.     */
  613.     var $columns = array();
  614.     
  615.     /**
  616.     * @var boolean Mark index for destruction
  617.     * @access private
  618.     */
  619.     var $drop = FALSE;
  620.     
  621.     /**
  622.     * Initializes the new dbIndex object.
  623.     *
  624.     * @param object $parent Parent object
  625.     * @param array $attributes Attributes
  626.     *
  627.     * @internal
  628.     */
  629.     function dbIndex( &$parent, $attributes = NULL ) {
  630.         $this->parent =& $parent;
  631.         
  632.         $this->name = $this->prefix ($attributes['NAME']);
  633.     }
  634.     
  635.     /**
  636.     * XML Callback to process start elements
  637.     *
  638.     * Processes XML opening tags. 
  639.     * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. 
  640.     *
  641.     * @access private
  642.     */
  643.     function _tag_open( &$parser, $tag, $attributes ) {
  644.         $this->currentElement = strtoupper( $tag );
  645.         
  646.         switch( $this->currentElement ) {
  647.             case 'DROP':
  648.                 $this->drop();
  649.                 break;
  650.             case 'CLUSTERED':
  651.             case 'BITMAP':
  652.             case 'UNIQUE':
  653.             case 'FULLTEXT':
  654.             case 'HASH':
  655.                 // Add index Option
  656.                 $this->addIndexOpt( $this->currentElement );
  657.                 break;
  658.             default:
  659.                 // print_r( array( $tag, $attributes ) );
  660.         }
  661.     }
  662.     
  663.     /**
  664.     * XML Callback to process CDATA elements
  665.     *
  666.     * Processes XML cdata.
  667.     *
  668.     * @access private
  669.     */
  670.     function _tag_cdata( &$parser, $cdata ) {
  671.         switch( $this->currentElement ) {
  672.             // Index field name
  673.             case 'COL':
  674.                 $this->addField( $cdata );
  675.                 break;
  676.             default:
  677.                 
  678.         }
  679.     }
  680.     
  681.     /**
  682.     * XML Callback to process end elements
  683.     *
  684.     * @access private
  685.     */
  686.     function _tag_close( &$parser, $tag ) {
  687.         $this->currentElement = '';
  688.         
  689.         switch( strtoupper( $tag ) ) {
  690.             case 'INDEX':
  691.                 xml_set_object( $parser, $this->parent );
  692.                 break;
  693.         }
  694.     }
  695.     
  696.     /**
  697.     * Adds a field to the index
  698.     *
  699.     * @param string $name Field name
  700.     * @return string Field list
  701.     */
  702.     function addField( $name ) {
  703.         $this->columns[$this->FieldID( $name )] = $name;
  704.         
  705.         // Return the field list
  706.         return $this->columns;
  707.     }
  708.     
  709.     /**
  710.     * Adds options to the index
  711.     *
  712.     * @param string $opt Comma-separated list of index options.
  713.     * @return string Option list
  714.     */
  715.     function addIndexOpt( $opt ) {
  716.         $this->opts[] = $opt;
  717.         
  718.         // Return the options list
  719.         return $this->opts;
  720.     }
  721.     
  722.     /**
  723.     * Generates the SQL that will create the index in the database
  724.     *
  725.     * @param object $xmls adoSchema object
  726.     * @return array Array containing index creation SQL
  727.     */
  728.     function create( &$xmls ) {
  729.         if( $this->drop ) {
  730.             return NULL;
  731.         }
  732.         
  733.         // eliminate any columns that aren't in the table
  734.         foreach( $this->columns as $id => $col ) {
  735.             if( !isset( $this->parent->fields[$id] ) ) {
  736.                 unset( $this->columns[$id] );
  737.             }
  738.         }
  739.         
  740.         return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
  741.     }
  742.     
  743.     /**
  744.     * Marks an index for destruction
  745.     */
  746.     function drop() {
  747.         $this->drop = TRUE;
  748.     }
  749. }
  750.  
  751. /**
  752. * Creates a data object in ADOdb's datadict format
  753. *
  754. * This class stores information about table data.
  755. *
  756. * @package axmls
  757. * @access private
  758. */
  759. class dbData extends dbObject {
  760.     
  761.     var $data = array();
  762.     
  763.     var $row;
  764.     
  765.     /**
  766.     * Initializes the new dbIndex object.
  767.     *
  768.     * @param object $parent Parent object
  769.     * @param array $attributes Attributes
  770.     *
  771.     * @internal
  772.     */
  773.     function dbData( &$parent, $attributes = NULL ) {
  774.         $this->parent =& $parent;
  775.     }
  776.     
  777.     /**
  778.     * XML Callback to process start elements
  779.     *
  780.     * Processes XML opening tags. 
  781.     * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. 
  782.     *
  783.     * @access private
  784.     */
  785.     function _tag_open( &$parser, $tag, $attributes ) {
  786.         $this->currentElement = strtoupper( $tag );
  787.         
  788.         switch( $this->currentElement ) {
  789.             case 'ROW':
  790.                 $this->row = count( $this->data );
  791.                 $this->data[$this->row] = array();
  792.                 break;
  793.             case 'F':
  794.                 $this->addField($attributes);
  795.             default:
  796.                 // print_r( array( $tag, $attributes ) );
  797.         }
  798.     }
  799.     
  800.     /**
  801.     * XML Callback to process CDATA elements
  802.     *
  803.     * Processes XML cdata.
  804.     *
  805.     * @access private
  806.     */
  807.     function _tag_cdata( &$parser, $cdata ) {
  808.         switch( $this->currentElement ) {
  809.             // Index field name
  810.             case 'F':
  811.                 $this->addData( $cdata );
  812.                 break;
  813.             default:
  814.                 
  815.         }
  816.     }
  817.     
  818.     /**
  819.     * XML Callback to process end elements
  820.     *
  821.     * @access private
  822.     */
  823.     function _tag_close( &$parser, $tag ) {
  824.         $this->currentElement = '';
  825.         
  826.         switch( strtoupper( $tag ) ) {
  827.             case 'DATA':
  828.                 xml_set_object( $parser, $this->parent );
  829.                 break;
  830.         }
  831.     }
  832.     
  833.     /**
  834.     * Adds a field to the index
  835.     *
  836.     * @param string $name Field name
  837.     * @return string Field list
  838.     */
  839.     function addField( $attributes ) {
  840.         if( isset( $attributes['NAME'] ) ) {
  841.             $name = $attributes['NAME'];
  842.         } else {
  843.             $name = count($this->data[$this->row]);
  844.         }
  845.         
  846.         // Set the field index so we know where we are
  847.         $this->current_field = $this->FieldID( $name );
  848.     }
  849.     
  850.     /**
  851.     * Adds options to the index
  852.     *
  853.     * @param string $opt Comma-separated list of index options.
  854.     * @return string Option list
  855.     */
  856.     function addData( $cdata ) {
  857.         if( !isset( $this->data[$this->row] ) ) {
  858.             $this->data[$this->row] = array();
  859.         }
  860.         
  861.         if( !isset( $this->data[$this->row][$this->current_field] ) ) {
  862.             $this->data[$this->row][$this->current_field] = '';
  863.         }
  864.         
  865.         $this->data[$this->row][$this->current_field] .= $cdata;
  866.     }
  867.     
  868.     /**
  869.     * Generates the SQL that will create the index in the database
  870.     *
  871.     * @param object $xmls adoSchema object
  872.     * @return array Array containing index creation SQL
  873.     */
  874.     function create( &$xmls ) {
  875.         $table = $xmls->dict->TableName($this->parent->name);
  876.         $table_field_count = count($this->parent->fields);
  877.         $sql = array();
  878.         
  879.         // eliminate any columns that aren't in the table
  880.         foreach( $this->data as $row ) {
  881.             $table_fields = $this->parent->fields;
  882.             $fields = array();
  883.             
  884.             foreach( $row as $field_id => $field_data ) {
  885.                 if( !array_key_exists( $field_id, $table_fields ) ) {
  886.                     if( is_numeric( $field_id ) ) {
  887.                         $field_id = reset( array_keys( $table_fields ) );
  888.                     } else {
  889.                         continue;
  890.                     }
  891.                 }
  892.                 
  893.                 $name = $table_fields[$field_id]['NAME'];
  894.                 
  895.                 switch( $table_fields[$field_id]['TYPE'] ) {
  896.                     case 'C':
  897.                     case 'C2':
  898.                     case 'X':
  899.                     case 'X2':
  900.                         $fields[$name] = $xmls->db->qstr( $field_data );
  901.                         break;
  902.                     case 'I':
  903.                     case 'I1':
  904.                     case 'I2':
  905.                     case 'I4':
  906.                     case 'I8':
  907.                         $fields[$name] = intval($field_data);
  908.                         break;
  909.                     default:
  910.                         $fields[$name] = $field_data;
  911.                 }
  912.                 
  913.                 unset($table_fields[$field_id]);
  914.             }
  915.             
  916.             // check that at least 1 column is specified
  917.             if( empty( $fields ) ) {
  918.                 continue;
  919.             }
  920.             
  921.             // check that no required columns are missing
  922.             if( count( $fields ) < $table_field_count ) {
  923.                 foreach( $table_fields as $field ) {
  924.                     if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
  925.                         continue(2);
  926.                     }
  927.                 }
  928.             }
  929.             
  930.             $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
  931.         }
  932.         
  933.         return $sql;
  934.     }
  935. }
  936.  
  937. /**
  938. * Creates the SQL to execute a list of provided SQL queries
  939. *
  940. * @package axmls
  941. * @access private
  942. */
  943. class dbQuerySet extends dbObject {
  944.     
  945.     /**
  946.     * @var array    List of SQL queries
  947.     */
  948.     var $queries = array();
  949.     
  950.     /**
  951.     * @var string    String used to build of a query line by line
  952.     */
  953.     var $query;
  954.     
  955.     /**
  956.     * @var string    Query prefix key
  957.     */
  958.     var $prefixKey = '';
  959.     
  960.     /**
  961.     * @var boolean    Auto prefix enable (TRUE)
  962.     */
  963.     var $prefixMethod = 'AUTO';
  964.     
  965.     /**
  966.     * Initializes the query set.
  967.     *
  968.     * @param object $parent Parent object
  969.     * @param array $attributes Attributes
  970.     */
  971.     function dbQuerySet( &$parent, $attributes = NULL ) {
  972.         $this->parent =& $parent;
  973.             
  974.         // Overrides the manual prefix key
  975.         if( isset( $attributes['KEY'] ) ) {
  976.             $this->prefixKey = $attributes['KEY'];
  977.         }
  978.         
  979.         $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
  980.         
  981.         // Enables or disables automatic prefix prepending
  982.         switch( $prefixMethod ) {
  983.             case 'AUTO':
  984.                 $this->prefixMethod = 'AUTO';
  985.                 break;
  986.             case 'MANUAL':
  987.                 $this->prefixMethod = 'MANUAL';
  988.                 break;
  989.             case 'NONE':
  990.                 $this->prefixMethod = 'NONE';
  991.                 break;
  992.         }
  993.     }
  994.     
  995.     /**
  996.     * XML Callback to process start elements. Elements currently 
  997.     * processed are: QUERY. 
  998.     *
  999.     * @access private
  1000.     */
  1001.     function _tag_open( &$parser, $tag, $attributes ) {
  1002.         $this->currentElement = strtoupper( $tag );
  1003.         
  1004.         switch( $this->currentElement ) {
  1005.             case 'QUERY':
  1006.                 // Create a new query in a SQL queryset.
  1007.                 // Ignore this query set if a platform is specified and it's different than the 
  1008.                 // current connection platform.
  1009.                 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  1010.                     $this->newQuery();
  1011.                 } else {
  1012.                     $this->discardQuery();
  1013.                 }
  1014.                 break;
  1015.             default:
  1016.                 // print_r( array( $tag, $attributes ) );
  1017.         }
  1018.     }
  1019.     
  1020.     /**
  1021.     * XML Callback to process CDATA elements
  1022.     */
  1023.     function _tag_cdata( &$parser, $cdata ) {
  1024.         switch( $this->currentElement ) {
  1025.             // Line of queryset SQL data
  1026.             case 'QUERY':
  1027.                 $this->buildQuery( $cdata );
  1028.                 break;
  1029.             default:
  1030.                 
  1031.         }
  1032.     }
  1033.     
  1034.     /**
  1035.     * XML Callback to process end elements
  1036.     *
  1037.     * @access private
  1038.     */
  1039.     function _tag_close( &$parser, $tag ) {
  1040.         $this->currentElement = '';
  1041.         
  1042.         switch( strtoupper( $tag ) ) {
  1043.             case 'QUERY':
  1044.                 // Add the finished query to the open query set.
  1045.                 $this->addQuery();
  1046.                 break;
  1047.             case 'SQL':
  1048.                 $this->parent->addSQL( $this->create( $this->parent ) );
  1049.                 xml_set_object( $parser, $this->parent );
  1050.                 $this->destroy();
  1051.                 break;
  1052.             default:
  1053.                 
  1054.         }
  1055.     }
  1056.     
  1057.     /**
  1058.     * Re-initializes the query.
  1059.     *
  1060.     * @return boolean TRUE
  1061.     */
  1062.     function newQuery() {
  1063.         $this->query = '';
  1064.         
  1065.         return TRUE;
  1066.     }
  1067.     
  1068.     /**
  1069.     * Discards the existing query.
  1070.     *
  1071.     * @return boolean TRUE
  1072.     */
  1073.     function discardQuery() {
  1074.         unset( $this->query );
  1075.         
  1076.         return TRUE;
  1077.     }
  1078.     
  1079.     /** 
  1080.     * Appends a line to a query that is being built line by line
  1081.     *
  1082.     * @param string $data Line of SQL data or NULL to initialize a new query
  1083.     * @return string SQL query string.
  1084.     */
  1085.     function buildQuery( $sql = NULL ) {
  1086.         if( !isset( $this->query ) OR empty( $sql ) ) {
  1087.             return FALSE;
  1088.         }
  1089.         
  1090.         $this->query .= $sql;
  1091.         
  1092.         return $this->query;
  1093.     }
  1094.     
  1095.     /**
  1096.     * Adds a completed query to the query list
  1097.     *
  1098.     * @return string    SQL of added query
  1099.     */
  1100.     function addQuery() {
  1101.         if( !isset( $this->query ) ) {
  1102.             return FALSE;
  1103.         }
  1104.         
  1105.         $this->queries[] = $return = trim($this->query);
  1106.         
  1107.         unset( $this->query );
  1108.         
  1109.         return $return;
  1110.     }
  1111.     
  1112.     /**
  1113.     * Creates and returns the current query set
  1114.     *
  1115.     * @param object $xmls adoSchema object
  1116.     * @return array Query set
  1117.     */
  1118.     function create( &$xmls ) {
  1119.         foreach( $this->queries as $id => $query ) {
  1120.             switch( $this->prefixMethod ) {
  1121.                 case 'AUTO':
  1122.                     // Enable auto prefix replacement
  1123.                     
  1124.                     // Process object prefix.
  1125.                     // Evaluate SQL statements to prepend prefix to objects
  1126.                     $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
  1127.                     $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
  1128.                     $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
  1129.                     
  1130.                     // SELECT statements aren't working yet
  1131.                     #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
  1132.                     
  1133.                 case 'MANUAL':
  1134.                     // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.
  1135.                     // If prefixKey is not set, we use the default constant XMLS_PREFIX
  1136.                     if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
  1137.                         // Enable prefix override
  1138.                         $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
  1139.                     } else {
  1140.                         // Use default replacement
  1141.                         $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
  1142.                     }
  1143.             }
  1144.             
  1145.             $this->queries[$id] = trim( $query );
  1146.         }
  1147.         
  1148.         // Return the query set array
  1149.         return $this->queries;
  1150.     }
  1151.     
  1152.     /**
  1153.     * Rebuilds the query with the prefix attached to any objects
  1154.     *
  1155.     * @param string $regex Regex used to add prefix
  1156.     * @param string $query SQL query string
  1157.     * @param string $prefix Prefix to be appended to tables, indices, etc.
  1158.     * @return string Prefixed SQL query string.
  1159.     */
  1160.     function prefixQuery( $regex, $query, $prefix = NULL ) {
  1161.         if( !isset( $prefix ) ) {
  1162.             return $query;
  1163.         }
  1164.         
  1165.         if( preg_match( $regex, $query, $match ) ) {
  1166.             $preamble = $match[1];
  1167.             $postamble = $match[5];
  1168.             $objectList = explode( ',', $match[3] );
  1169.             // $prefix = $prefix . '_';
  1170.             
  1171.             $prefixedList = '';
  1172.             
  1173.             foreach( $objectList as $object ) {
  1174.                 if( $prefixedList !== '' ) {
  1175.                     $prefixedList .= ', ';
  1176.                 }
  1177.                 
  1178.                 $prefixedList .= $prefix . trim( $object );
  1179.             }
  1180.             
  1181.             $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
  1182.         }
  1183.         
  1184.         return $query;
  1185.     }
  1186. }
  1187.  
  1188. /**
  1189. * Loads and parses an XML file, creating an array of "ready-to-run" SQL statements
  1190. * This class is used to load and parse the XML file, to create an array of SQL statements
  1191. * that can be used to build a database, and to build the database using the SQL array.
  1192. *
  1193. * @tutorial getting_started.pkg
  1194. *
  1195. * @author Richard Tango-Lowy & Dan Cech
  1196. * @version $Revision: 1.11 $
  1197. *
  1198. * @package axmls
  1199. */
  1200. class adoSchema {
  1201.     
  1202.     /**
  1203.     * @var array    Array containing SQL queries to generate all objects
  1204.     * @access private
  1205.     */
  1206.     var $sqlArray;
  1207.     
  1208.     /**
  1209.     * @var object    ADOdb connection object
  1210.     * @access private
  1211.     */
  1212.     var $db;
  1213.     
  1214.     /**
  1215.     * @var object    ADOdb Data Dictionary
  1216.     * @access private
  1217.     */
  1218.     var $dict;
  1219.     
  1220.     /**
  1221.     * @var string Current XML element
  1222.     * @access private
  1223.     */
  1224.     var $currentElement = '';
  1225.     
  1226.     /**
  1227.     * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
  1228.     * @access private
  1229.     */
  1230.     var $upgrade = '';
  1231.     
  1232.     /**
  1233.     * @var string Optional object prefix
  1234.     * @access private
  1235.     */
  1236.     var $objectPrefix = '';
  1237.     
  1238.     /**
  1239.     * @var long    Original Magic Quotes Runtime value
  1240.     * @access private
  1241.     */
  1242.     var $mgq;
  1243.     
  1244.     /**
  1245.     * @var long    System debug
  1246.     * @access private
  1247.     */
  1248.     var $debug;
  1249.     
  1250.     /**
  1251.     * @var string Regular expression to find schema version
  1252.     * @access private
  1253.     */
  1254.     var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
  1255.     
  1256.     /**
  1257.     * @var string Current schema version
  1258.     * @access private
  1259.     */
  1260.     var $schemaVersion;
  1261.     
  1262.     /**
  1263.     * @var int    Success of last Schema execution
  1264.     */
  1265.     var $success;
  1266.     
  1267.     /**
  1268.     * @var bool    Execute SQL inline as it is generated
  1269.     */
  1270.     var $executeInline;
  1271.     
  1272.     /**
  1273.     * @var bool    Continue SQL execution if errors occur
  1274.     */
  1275.     var $continueOnError;
  1276.     
  1277.     /**
  1278.     * Creates an adoSchema object
  1279.     *
  1280.     * Creating an adoSchema object is the first step in processing an XML schema.
  1281.     * The only parameter is an ADOdb database connection object, which must already
  1282.     * have been created.
  1283.     *
  1284.     * @param object $db ADOdb database connection object.
  1285.     */
  1286.     function adoSchema( &$db ) {
  1287.         // Initialize the environment
  1288.         $this->mgq = get_magic_quotes_runtime();
  1289.         set_magic_quotes_runtime(0);
  1290.         
  1291.         $this->db =& $db;
  1292.         $this->debug = $this->db->debug;
  1293.         $this->dict = NewDataDictionary( $this->db );
  1294.         $this->sqlArray = array();
  1295.         $this->schemaVersion = XMLS_SCHEMA_VERSION;
  1296.         $this->executeInline( XMLS_EXECUTE_INLINE );
  1297.         $this->continueOnError( XMLS_CONTINUE_ON_ERROR );
  1298.         $this->setUpgradeMethod();
  1299.     }
  1300.     
  1301.     /**
  1302.     * Sets the method to be used for upgrading an existing database
  1303.     *
  1304.     * Use this method to specify how existing database objects should be upgraded.
  1305.     * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to
  1306.     * alter each database object directly, REPLACE attempts to rebuild each object
  1307.     * from scratch, BEST attempts to determine the best upgrade method for each
  1308.     * object, and NONE disables upgrading.
  1309.     *
  1310.     * This method is not yet used by AXMLS, but exists for backward compatibility.
  1311.     * The ALTER method is automatically assumed when the adoSchema object is
  1312.     * instantiated; other upgrade methods are not currently supported.
  1313.     *
  1314.     * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)
  1315.     * @returns string Upgrade method used
  1316.     */
  1317.     function SetUpgradeMethod( $method = '' ) {
  1318.         if( !is_string( $method ) ) {
  1319.             return FALSE;
  1320.         }
  1321.         
  1322.         $method = strtoupper( $method );
  1323.         
  1324.         // Handle the upgrade methods
  1325.         switch( $method ) {
  1326.             case 'ALTER':
  1327.                 $this->upgrade = $method;
  1328.                 break;
  1329.             case 'REPLACE':
  1330.                 $this->upgrade = $method;
  1331.                 break;
  1332.             case 'BEST':
  1333.                 $this->upgrade = 'ALTER';
  1334.                 break;
  1335.             case 'NONE':
  1336.                 $this->upgrade = 'NONE';
  1337.                 break;
  1338.             default:
  1339.                 // Use default if no legitimate method is passed.
  1340.                 $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
  1341.         }
  1342.         
  1343.         return $this->upgrade;
  1344.     }
  1345.     
  1346.     /**
  1347.     * Enables/disables inline SQL execution.
  1348.     *
  1349.     * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),
  1350.     * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode
  1351.     * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()
  1352.     * to apply the schema to the database.
  1353.     *
  1354.     * @param bool $mode execute
  1355.     * @return bool current execution mode
  1356.     *
  1357.     * @see ParseSchema(), ExecuteSchema()
  1358.     */
  1359.     function ExecuteInline( $mode = NULL ) {
  1360.         if( is_bool( $mode ) ) {
  1361.             $this->executeInline = $mode;
  1362.         }
  1363.         
  1364.         return $this->executeInline;
  1365.     }
  1366.     
  1367.     /**
  1368.     * Enables/disables SQL continue on error.
  1369.     *
  1370.     * Call this method to enable or disable continuation of SQL execution if an error occurs.
  1371.     * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.
  1372.     * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing
  1373.     * of the schema will continue.
  1374.     *
  1375.     * @param bool $mode execute
  1376.     * @return bool current continueOnError mode
  1377.     *
  1378.     * @see addSQL(), ExecuteSchema()
  1379.     */
  1380.     function ContinueOnError( $mode = NULL ) {
  1381.         if( is_bool( $mode ) ) {
  1382.             $this->continueOnError = $mode;
  1383.         }
  1384.         
  1385.         return $this->continueOnError;
  1386.     }
  1387.     
  1388.     /**
  1389.     * Loads an XML schema from a file and converts it to SQL.
  1390.     *
  1391.     * Call this method to load the specified schema (see the DTD for the proper format) from
  1392.     * the filesystem and generate the SQL necessary to create the database described. 
  1393.     * @see ParseSchemaString()
  1394.     *
  1395.     * @param string $file Name of XML schema file.
  1396.     * @param bool $returnSchema Return schema rather than parsing.
  1397.     * @return array Array of SQL queries, ready to execute
  1398.     */
  1399.     function ParseSchema( $filename, $returnSchema = FALSE ) {
  1400.         return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
  1401.     }
  1402.     
  1403.     /**
  1404.     * Loads an XML schema from a file and converts it to SQL.
  1405.     *
  1406.     * Call this method to load the specified schema from a file (see the DTD for the proper format) 
  1407.     * and generate the SQL necessary to create the database described by the schema.
  1408.     *
  1409.     * @param string $file Name of XML schema file.
  1410.     * @param bool $returnSchema Return schema rather than parsing.
  1411.     * @return array Array of SQL queries, ready to execute.
  1412.     *
  1413.     * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()
  1414.     * @see ParseSchema(), ParseSchemaString()
  1415.     */
  1416.     function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
  1417.         // Open the file
  1418.         if( !($fp = fopen( $filename, 'r' )) ) {
  1419.             // die( 'Unable to open file' );
  1420.             return FALSE;
  1421.         }
  1422.         
  1423.         // do version detection here
  1424.         if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
  1425.             return FALSE;
  1426.         }
  1427.         
  1428.         if ( $returnSchema )
  1429.         {
  1430.             return $xmlstring;
  1431.         }
  1432.         
  1433.         $this->success = 2;
  1434.         
  1435.         $xmlParser = $this->create_parser();
  1436.         
  1437.         // Process the file
  1438.         while( $data = fread( $fp, 4096 ) ) {
  1439.             if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
  1440.                 die( sprintf(
  1441.                     "XML error: %s at line %d",
  1442.                     xml_error_string( xml_get_error_code( $xmlParser) ),
  1443.                     xml_get_current_line_number( $xmlParser)
  1444.                 ) );
  1445.             }
  1446.         }
  1447.         
  1448.         xml_parser_free( $xmlParser );
  1449.         
  1450.         return $this->sqlArray;
  1451.     }
  1452.     
  1453.     /**
  1454.     * Converts an XML schema string to SQL.
  1455.     *
  1456.     * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
  1457.     * and generate the SQL necessary to create the database described by the schema. 
  1458.     * @see ParseSchema()
  1459.     *
  1460.     * @param string $xmlstring XML schema string.
  1461.     * @param bool $returnSchema Return schema rather than parsing.
  1462.     * @return array Array of SQL queries, ready to execute.
  1463.     */
  1464.     function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
  1465.         if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
  1466.             return FALSE;
  1467.         }
  1468.         
  1469.         // do version detection here
  1470.         if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
  1471.             return FALSE;
  1472.         }
  1473.         
  1474.         if ( $returnSchema )
  1475.         {
  1476.             return $xmlstring;
  1477.         }
  1478.         
  1479.         $this->success = 2;
  1480.         
  1481.         $xmlParser = $this->create_parser();
  1482.         
  1483.         if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
  1484.             die( sprintf(
  1485.                 "XML error: %s at line %d",
  1486.                 xml_error_string( xml_get_error_code( $xmlParser) ),
  1487.                 xml_get_current_line_number( $xmlParser)
  1488.             ) );
  1489.         }
  1490.         
  1491.         xml_parser_free( $xmlParser );
  1492.         
  1493.         return $this->sqlArray;
  1494.     }
  1495.     
  1496.     /**
  1497.     * Loads an XML schema from a file and converts it to uninstallation SQL.
  1498.     *
  1499.     * Call this method to load the specified schema (see the DTD for the proper format) from
  1500.     * the filesystem and generate the SQL necessary to remove the database described.
  1501.     * @see RemoveSchemaString()
  1502.     *
  1503.     * @param string $file Name of XML schema file.
  1504.     * @param bool $returnSchema Return schema rather than parsing.
  1505.     * @return array Array of SQL queries, ready to execute
  1506.     */
  1507.     function RemoveSchema( $filename, $returnSchema = FALSE ) {
  1508.         return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
  1509.     }
  1510.     
  1511.     /**
  1512.     * Converts an XML schema string to uninstallation SQL.
  1513.     *
  1514.     * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
  1515.     * and generate the SQL necessary to uninstall the database described by the schema. 
  1516.     * @see RemoveSchema()
  1517.     *
  1518.     * @param string $schema XML schema string.
  1519.     * @param bool $returnSchema Return schema rather than parsing.
  1520.     * @return array Array of SQL queries, ready to execute.
  1521.     */
  1522.     function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
  1523.         
  1524.         // grab current version
  1525.         if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
  1526.             return FALSE;
  1527.         }
  1528.         
  1529.         return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
  1530.     }
  1531.     
  1532.     /**
  1533.     * Applies the current XML schema to the database (post execution).
  1534.     *
  1535.     * Call this method to apply the current schema (generally created by calling 
  1536.     * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, 
  1537.     * and executing other SQL specified in the schema) after parsing.
  1538.     * @see ParseSchema(), ParseSchemaString(), ExecuteInline()
  1539.     *
  1540.     * @param array $sqlArray Array of SQL statements that will be applied rather than
  1541.     *        the current schema.
  1542.     * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.
  1543.     * @returns integer 0 if failure, 1 if errors, 2 if successful.
  1544.     */
  1545.     function ExecuteSchema( $sqlArray = NULL, $continueOnErr =  NULL ) {
  1546.         if( !is_bool( $continueOnErr ) ) {
  1547.             $continueOnErr = $this->ContinueOnError();
  1548.         }
  1549.         
  1550.         if( !isset( $sqlArray ) ) {
  1551.             $sqlArray = $this->sqlArray;
  1552.         }
  1553.         
  1554.         if( !is_array( $sqlArray ) ) {
  1555.             $this->success = 0;
  1556.         } else {
  1557.             $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
  1558.         }
  1559.         
  1560.         return $this->success;
  1561.     }
  1562.     
  1563.     /**
  1564.     * Returns the current SQL array. 
  1565.     *
  1566.     * Call this method to fetch the array of SQL queries resulting from 
  1567.     * ParseSchema() or ParseSchemaString(). 
  1568.     *
  1569.     * @param string $format Format: HTML, TEXT, or NONE (PHP array)
  1570.     * @return array Array of SQL statements or FALSE if an error occurs
  1571.     */
  1572.     function PrintSQL( $format = 'NONE' ) {
  1573.         return $this->getSQL( $format, $sqlArray );
  1574.     }
  1575.     
  1576.     /**
  1577.     * Saves the current SQL array to the local filesystem as a list of SQL queries.
  1578.     *
  1579.     * Call this method to save the array of SQL queries (generally resulting from a
  1580.     * parsed XML schema) to the filesystem.
  1581.     *
  1582.     * @param string $filename Path and name where the file should be saved.
  1583.     * @return boolean TRUE if save is successful, else FALSE. 
  1584.     */
  1585.     function SaveSQL( $filename = './schema.sql' ) {
  1586.         
  1587.         if( !isset( $sqlArray ) ) {
  1588.             $sqlArray = $this->sqlArray;
  1589.         }
  1590.         if( !isset( $sqlArray ) ) {
  1591.             return FALSE;
  1592.         }
  1593.         
  1594.         $fp = fopen( $filename, "w" );
  1595.         
  1596.         foreach( $sqlArray as $key => $query ) {
  1597.             fwrite( $fp, $query . ";\n" );
  1598.         }
  1599.         fclose( $fp );
  1600.     }
  1601.     
  1602.     /**
  1603.     * Create an xml parser
  1604.     *
  1605.     * @return object PHP XML parser object
  1606.     *
  1607.     * @access private
  1608.     */
  1609.     function &create_parser() {
  1610.         // Create the parser
  1611.         $xmlParser = xml_parser_create();
  1612.         xml_set_object( $xmlParser, $this );
  1613.         
  1614.         // Initialize the XML callback functions
  1615.         xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
  1616.         xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
  1617.         
  1618.         return $xmlParser;
  1619.     }
  1620.     
  1621.     /**
  1622.     * XML Callback to process start elements
  1623.     *
  1624.     * @access private
  1625.     */
  1626.     function _tag_open( &$parser, $tag, $attributes ) {
  1627.         switch( strtoupper( $tag ) ) {
  1628.             case 'TABLE':
  1629.                 $this->obj = new dbTable( $this, $attributes );
  1630.                 xml_set_object( $parser, $this->obj );
  1631.                 break;
  1632.             case 'SQL':
  1633.                 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
  1634.                     $this->obj = new dbQuerySet( $this, $attributes );
  1635.                     xml_set_object( $parser, $this->obj );
  1636.                 }
  1637.                 break;
  1638.             default:
  1639.                 // print_r( array( $tag, $attributes ) );
  1640.         }
  1641.         
  1642.     }
  1643.     
  1644.     /**
  1645.     * XML Callback to process CDATA elements
  1646.     *
  1647.     * @access private
  1648.     */
  1649.     function _tag_cdata( &$parser, $cdata ) {
  1650.     }
  1651.     
  1652.     /**
  1653.     * XML Callback to process end elements
  1654.     *
  1655.     * @access private
  1656.     * @internal
  1657.     */
  1658.     function _tag_close( &$parser, $tag ) {
  1659.         
  1660.     }
  1661.     
  1662.     /**
  1663.     * Converts an XML schema string to the specified DTD version.
  1664.     *
  1665.     * Call this method to convert a string containing an XML schema to a different AXMLS
  1666.     * DTD version. For instance, to convert a schema created for an pre-1.0 version for 
  1667.     * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
  1668.     * parameter is specified, the schema will be converted to the current DTD version. 
  1669.     * If the newFile parameter is provided, the converted schema will be written to the specified
  1670.     * file.
  1671.     * @see ConvertSchemaFile()
  1672.     *
  1673.     * @param string $schema String containing XML schema that will be converted.
  1674.     * @param string $newVersion DTD version to convert to.
  1675.     * @param string $newFile File name of (converted) output file.
  1676.     * @return string Converted XML schema or FALSE if an error occurs.
  1677.     */
  1678.     function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
  1679.         
  1680.         // grab current version
  1681.         if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
  1682.             return FALSE;
  1683.         }
  1684.         
  1685.         if( !isset ($newVersion) ) {
  1686.             $newVersion = $this->schemaVersion;
  1687.         }
  1688.         
  1689.         if( $version == $newVersion ) {
  1690.             $result = $schema;
  1691.         } else {
  1692.             $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
  1693.         }
  1694.         
  1695.         if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
  1696.             fwrite( $fp, $result );
  1697.             fclose( $fp );
  1698.         }
  1699.         
  1700.         return $result;
  1701.     }
  1702.     
  1703.     /**
  1704.     * Converts an XML schema file to the specified DTD version.
  1705.     *
  1706.     * Call this method to convert the specified XML schema file to a different AXMLS
  1707.     * DTD version. For instance, to convert a schema created for an pre-1.0 version for 
  1708.     * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version 
  1709.     * parameter is specified, the schema will be converted to the current DTD version. 
  1710.     * If the newFile parameter is provided, the converted schema will be written to the specified
  1711.     * file.
  1712.     * @see ConvertSchemaString()
  1713.     *
  1714.     * @param string $filename Name of XML schema file that will be converted.
  1715.     * @param string $newVersion DTD version to convert to.
  1716.     * @param string $newFile File name of (converted) output file.
  1717.     * @return string Converted XML schema or FALSE if an error occurs.
  1718.     */
  1719.     function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
  1720.         
  1721.         // grab current version
  1722.         if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
  1723.             return FALSE;
  1724.         }
  1725.         
  1726.         if( !isset ($newVersion) ) {
  1727.             $newVersion = $this->schemaVersion;
  1728.         }
  1729.         
  1730.         if( $version == $newVersion ) {
  1731.             $result = file_get_contents( $filename );
  1732.             
  1733.             // remove unicode BOM if present
  1734.             if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
  1735.                 $result = substr( $result, 3 );
  1736.             }
  1737.         } else {
  1738.             $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
  1739.         }
  1740.         
  1741.         if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
  1742.             fwrite( $fp, $result );
  1743.             fclose( $fp );
  1744.         }
  1745.         
  1746.         return $result;
  1747.     }
  1748.     
  1749.     function TransformSchema( $schema, $xsl, $schematype='string' )
  1750.     {
  1751.         // Fail if XSLT extension is not available
  1752.         if( ! function_exists( 'xslt_create' ) ) {
  1753.             return FALSE;
  1754.         }
  1755.         
  1756.         $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
  1757.         
  1758.         // look for xsl
  1759.         if( !is_readable( $xsl_file ) ) {
  1760.             return FALSE;
  1761.         }
  1762.         
  1763.         switch( $schematype )
  1764.         {
  1765.             case 'file':
  1766.                 if( !is_readable( $schema ) ) {
  1767.                     return FALSE;
  1768.                 }
  1769.                 
  1770.                 $schema = file_get_contents( $schema );
  1771.                 break;
  1772.             case 'string':
  1773.             default:
  1774.                 if( !is_string( $schema ) ) {
  1775.                     return FALSE;
  1776.                 }
  1777.         }
  1778.         
  1779.         $arguments = array (
  1780.             '/_xml' => $schema,
  1781.             '/_xsl' => file_get_contents( $xsl_file )
  1782.         );
  1783.         
  1784.         // create an XSLT processor
  1785.         $xh = xslt_create ();
  1786.         
  1787.         // set error handler
  1788.         xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
  1789.         
  1790.         // process the schema
  1791.         $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); 
  1792.         
  1793.         xslt_free ($xh);
  1794.         
  1795.         return $result;
  1796.     }
  1797.     
  1798.     /**
  1799.     * Processes XSLT transformation errors
  1800.     *
  1801.     * @param object $parser XML parser object
  1802.     * @param integer $errno Error number
  1803.     * @param integer $level Error level
  1804.     * @param array $fields Error information fields
  1805.     *
  1806.     * @access private
  1807.     */
  1808.     function xslt_error_handler( $parser, $errno, $level, $fields ) {
  1809.         if( is_array( $fields ) ) {
  1810.             $msg = array(
  1811.                 'Message Type' => ucfirst( $fields['msgtype'] ),
  1812.                 'Message Code' => $fields['code'],
  1813.                 'Message' => $fields['msg'],
  1814.                 'Error Number' => $errno,
  1815.                 'Level' => $level
  1816.             );
  1817.             
  1818.             switch( $fields['URI'] ) {
  1819.                 case 'arg:/_xml':
  1820.                     $msg['Input'] = 'XML';
  1821.                     break;
  1822.                 case 'arg:/_xsl':
  1823.                     $msg['Input'] = 'XSL';
  1824.                     break;
  1825.                 default:
  1826.                     $msg['Input'] = $fields['URI'];
  1827.             }
  1828.             
  1829.             $msg['Line'] = $fields['line'];
  1830.         } else {
  1831.             $msg = array(
  1832.                 'Message Type' => 'Error',
  1833.                 'Error Number' => $errno,
  1834.                 'Level' => $level,
  1835.                 'Fields' => var_export( $fields, TRUE )
  1836.             );
  1837.         }
  1838.         
  1839.         $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
  1840.                        . '<table>' . "\n";
  1841.         
  1842.         foreach( $msg as $label => $details ) {
  1843.             $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
  1844.         }
  1845.         
  1846.         $error_details .= '</table>';
  1847.         
  1848.         trigger_error( $error_details, E_USER_ERROR );
  1849.     }
  1850.     
  1851.     /**
  1852.     * Returns the AXMLS Schema Version of the requested XML schema file.
  1853.     *
  1854.     * Call this method to obtain the AXMLS DTD version of the requested XML schema file.
  1855.     * @see SchemaStringVersion()
  1856.     *
  1857.     * @param string $filename AXMLS schema file
  1858.     * @return string Schema version number or FALSE on error
  1859.     */
  1860.     function SchemaFileVersion( $filename ) {
  1861.         // Open the file
  1862.         if( !($fp = fopen( $filename, 'r' )) ) {
  1863.             // die( 'Unable to open file' );
  1864.             return FALSE;
  1865.         }
  1866.         
  1867.         // Process the file
  1868.         while( $data = fread( $fp, 4096 ) ) {
  1869.             if( preg_match( $this->versionRegex, $data, $matches ) ) {
  1870.                 return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
  1871.             }
  1872.         }
  1873.         
  1874.         return FALSE;
  1875.     }
  1876.     
  1877.     /**
  1878.     * Returns the AXMLS Schema Version of the provided XML schema string.
  1879.     *
  1880.     * Call this method to obtain the AXMLS DTD version of the provided XML schema string.
  1881.     * @see SchemaFileVersion()
  1882.     *
  1883.     * @param string $xmlstring XML schema string
  1884.     * @return string Schema version number or FALSE on error
  1885.     */
  1886.     function SchemaStringVersion( $xmlstring ) {
  1887.         if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
  1888.             return FALSE;
  1889.         }
  1890.         
  1891.         if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
  1892.             return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
  1893.         }
  1894.         
  1895.         return FALSE;
  1896.     }
  1897.     
  1898.     /**
  1899.     * Extracts an XML schema from an existing database.
  1900.     *
  1901.     * Call this method to create an XML schema string from an existing database.
  1902.     * If the data parameter is set to TRUE, AXMLS will include the data from the database
  1903.     * in the schema. 
  1904.     *
  1905.     * @param boolean $data Include data in schema dump
  1906.     * @return string Generated XML schema
  1907.     */
  1908.     function ExtractSchema( $data = FALSE ) {
  1909.         $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
  1910.         
  1911.         $schema = '<?xml version="1.0"?>' . "\n"
  1912.                 . '<schema version="' . $this->schemaVersion . '">' . "\n";
  1913.         
  1914.         if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) {
  1915.             foreach( $tables as $table ) {
  1916.                 $schema .= '    <table name="' . $table . '">' . "\n";
  1917.                 
  1918.                 // grab details from database
  1919.                 $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' );
  1920.                 $fields = $this->db->MetaColumns( $table );
  1921.                 $indexes = $this->db->MetaIndexes( $table );
  1922.                 
  1923.                 if( is_array( $fields ) ) {
  1924.                     foreach( $fields as $details ) {
  1925.                         $extra = '';
  1926.                         $content = array();
  1927.                         
  1928.                         if( $details->max_length > 0 ) {
  1929.                             $extra .= ' size="' . $details->max_length . '"';
  1930.                         }
  1931.                         
  1932.                         if( $details->primary_key ) {
  1933.                             $content[] = '<KEY/>';
  1934.                         } elseif( $details->not_null ) {
  1935.                             $content[] = '<NOTNULL/>';
  1936.                         }
  1937.                         
  1938.                         if( $details->has_default ) {
  1939.                             $content[] = '<DEFAULT value="' . $details->default_value . '"/>';
  1940.                         }
  1941.                         
  1942.                         if( $details->auto_increment ) {
  1943.                             $content[] = '<AUTOINCREMENT/>';
  1944.                         }
  1945.                         
  1946.                         // this stops the creation of 'R' columns,
  1947.                         // AUTOINCREMENT is used to create auto columns
  1948.                         $details->primary_key = 0;
  1949.                         $type = $rs->MetaType( $details );
  1950.                         
  1951.                         $schema .= '        <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>';
  1952.                         
  1953.                         if( !empty( $content ) ) {
  1954.                             $schema .= "\n            " . implode( "\n            ", $content ) . "\n        ";
  1955.                         }
  1956.                         
  1957.                         $schema .= '</field>' . "\n";
  1958.                     }
  1959.                 }
  1960.                 
  1961.                 if( is_array( $indexes ) ) {
  1962.                     foreach( $indexes as $index => $details ) {
  1963.                         $schema .= '        <index name="' . $index . '">' . "\n";
  1964.                         
  1965.                         if( $details['unique'] ) {
  1966.                             $schema .= '            <UNIQUE/>' . "\n";
  1967.                         }
  1968.                         
  1969.                         foreach( $details['columns'] as $column ) {
  1970.                             $schema .= '            <col>' . $column . '</col>' . "\n";
  1971.                         }
  1972.                         
  1973.                         $schema .= '        </index>' . "\n";
  1974.                     }
  1975.                 }
  1976.                 
  1977.                 if( $data ) {
  1978.                     $rs = $this->db->Execute( 'SELECT * FROM ' . $table );
  1979.                     
  1980.                     if( is_object( $rs ) ) {
  1981.                         $schema .= '        <data>' . "\n";
  1982.                         
  1983.                         while( $row = $rs->FetchRow() ) {
  1984.                             foreach( $row as $key => $val ) {
  1985.                                 $row[$key] = htmlentities($val);
  1986.                             }
  1987.                             
  1988.                             $schema .= '            <row><f>' . implode( '</f><f>', $row ) . '</f></row>' . "\n";
  1989.                         }
  1990.                         
  1991.                         $schema .= '        </data>' . "\n";
  1992.                     }
  1993.                 }
  1994.                 
  1995.                 $schema .= '    </table>' . "\n";
  1996.             }
  1997.         }
  1998.         
  1999.         $this->db->SetFetchMode( $old_mode );
  2000.         
  2001.         $schema .= '</schema>';
  2002.         return $schema;
  2003.     }
  2004.     
  2005.     /**
  2006.     * Sets a prefix for database objects
  2007.     *
  2008.     * Call this method to set a standard prefix that will be prepended to all database tables 
  2009.     * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.
  2010.     *
  2011.     * @param string $prefix Prefix that will be prepended.
  2012.     * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.
  2013.     * @return boolean TRUE if successful, else FALSE
  2014.     */
  2015.     function SetPrefix( $prefix = '', $underscore = TRUE ) {
  2016.         switch( TRUE ) {
  2017.             // clear prefix
  2018.             case empty( $prefix ):
  2019.                 logMsg( 'Cleared prefix' );
  2020.                 $this->objectPrefix = '';
  2021.                 return TRUE;
  2022.             // prefix too long
  2023.             case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:
  2024.             // prefix contains invalid characters
  2025.             case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):
  2026.                 logMsg( 'Invalid prefix: ' . $prefix );
  2027.                 return FALSE;
  2028.         }
  2029.         
  2030.         if( $underscore AND substr( $prefix, -1 ) != '_' ) {
  2031.             $prefix .= '_';
  2032.         }
  2033.         
  2034.         // prefix valid
  2035.         logMsg( 'Set prefix: ' . $prefix );
  2036.         $this->objectPrefix = $prefix;
  2037.         return TRUE;
  2038.     }
  2039.     
  2040.     /**
  2041.     * Returns an object name with the current prefix prepended.
  2042.     *
  2043.     * @param string    $name Name
  2044.     * @return string    Prefixed name
  2045.     *
  2046.     * @access private
  2047.     */
  2048.     function prefix( $name = '' ) {
  2049.         // if prefix is set
  2050.         if( !empty( $this->objectPrefix ) ) {
  2051.             // Prepend the object prefix to the table name
  2052.             // prepend after quote if used
  2053.             return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );
  2054.         }
  2055.         
  2056.         // No prefix set. Use name provided.
  2057.         return $name;
  2058.     }
  2059.     
  2060.     /**
  2061.     * Checks if element references a specific platform
  2062.     *
  2063.     * @param string $platform Requested platform
  2064.     * @returns boolean TRUE if platform check succeeds
  2065.     *
  2066.     * @access private
  2067.     */
  2068.     function supportedPlatform( $platform = NULL ) {
  2069.         $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/';
  2070.         
  2071.         if( !isset( $platform ) OR preg_match( $regex, $platform ) ) {
  2072.             logMsg( "Platform $platform is supported" );
  2073.             return TRUE;
  2074.         } else {
  2075.             logMsg( "Platform $platform is NOT supported" );
  2076.             return FALSE;
  2077.         }
  2078.     }
  2079.     
  2080.     /**
  2081.     * Clears the array of generated SQL.
  2082.     *
  2083.     * @access private
  2084.     */
  2085.     function clearSQL() {
  2086.         $this->sqlArray = array();
  2087.     }
  2088.     
  2089.     /**
  2090.     * Adds SQL into the SQL array.
  2091.     *
  2092.     * @param mixed $sql SQL to Add
  2093.     * @return boolean TRUE if successful, else FALSE.
  2094.     *
  2095.     * @access private
  2096.     */    
  2097.     function addSQL( $sql = NULL ) {
  2098.         if( is_array( $sql ) ) {
  2099.             foreach( $sql as $line ) {
  2100.                 $this->addSQL( $line );
  2101.             }
  2102.             
  2103.             return TRUE;
  2104.         }
  2105.         
  2106.         if( is_string( $sql ) ) {
  2107.             $this->sqlArray[] = $sql;
  2108.             
  2109.             // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.
  2110.             if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {
  2111.                 $saved = $this->db->debug;
  2112.                 $this->db->debug = $this->debug;
  2113.                 $ok = $this->db->Execute( $sql );
  2114.                 $this->db->debug = $saved;
  2115.                 
  2116.                 if( !$ok ) {
  2117.                     if( $this->debug ) {
  2118.                         ADOConnection::outp( $this->db->ErrorMsg() );
  2119.                     }
  2120.                     
  2121.                     $this->success = 1;
  2122.                 }
  2123.             }
  2124.             
  2125.             return TRUE;
  2126.         }
  2127.         
  2128.         return FALSE;
  2129.     }
  2130.     
  2131.     /**
  2132.     * Gets the SQL array in the specified format.
  2133.     *
  2134.     * @param string $format Format
  2135.     * @return mixed SQL
  2136.     *    
  2137.     * @access private
  2138.     */
  2139.     function getSQL( $format = NULL, $sqlArray = NULL ) {
  2140.         if( !is_array( $sqlArray ) ) {
  2141.             $sqlArray = $this->sqlArray;
  2142.         }
  2143.         
  2144.         if( !is_array( $sqlArray ) ) {
  2145.             return FALSE;
  2146.         }
  2147.         
  2148.         switch( strtolower( $format ) ) {
  2149.             case 'string':
  2150.             case 'text':
  2151.                 return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';
  2152.             case'html':
  2153.                 return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';
  2154.         }
  2155.         
  2156.         return $this->sqlArray;
  2157.     }
  2158.     
  2159.     /**
  2160.     * Destroys an adoSchema object.
  2161.     *
  2162.     * Call this method to clean up after an adoSchema object that is no longer in use.
  2163.     * @deprecated adoSchema now cleans up automatically.
  2164.     */
  2165.     function Destroy() {
  2166.         set_magic_quotes_runtime( $this->mgq );
  2167.         unset( $this );
  2168.     }
  2169. }
  2170.  
  2171. /**
  2172. * Message logging function
  2173. *
  2174. * @access private
  2175. */
  2176. function logMsg( $msg, $title = NULL, $force = FALSE ) {
  2177.     if( XMLS_DEBUG or $force ) {
  2178.         echo '<pre>';
  2179.         
  2180.         if( isset( $title ) ) {
  2181.             echo '<h3>' . htmlentities( $title ) . '</h3>';
  2182.         }
  2183.         
  2184.         if( is_object( $this ) ) {
  2185.             echo '[' . get_class( $this ) . '] ';
  2186.         }
  2187.         
  2188.         print_r( $msg );
  2189.         
  2190.         echo '</pre>';
  2191.     }
  2192. }
  2193. ?>